/**
 * 
 */
package com.vision.core.sv.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.persistence.Column;

/**
 * Simple reflection-based implementation of {@link IBeanValidator}.
 * 
 * @author Mark
 *
 */
public class BeanValidator<T> implements IBeanValidator<T> {

	private Class<T> clazz;
	
	// Cache of the fields and methods with the javax.persistence.Column annotation.
	private List<Field> fields;
	private List<Method> methods;
	
	public BeanValidator(Class<T> clazz) {
		this.clazz = clazz;
	}

	@Override
	public Class<T> getBeanClass() {
		return clazz;
	}

	/**
	 * Goes through each field and method in <tt>object</tt>, checks if the {@link Column}
	 * annotation is present, and validates the value of the field or the return value
	 * of the method against the specified constraints in the {@link Column} annotation.
	 * 
	 * @param object
	 */
	@Override
	public List<String> validate(T object) {
		List<String> list = new ArrayList<String>(0);
		
		try {
			if (fields == null)
				fields = new ArrayList<Field>(Arrays.asList(clazz.getDeclaredFields()));
			
			for (int i = 0; i < fields.size(); i++) {
				Field field = fields.get(i);
				Column column = field.getAnnotation(Column.class);
				if (column != null)
					doCheck(column, field.getType(), field.get(object));
				else
					fields.remove(i--);
			}
			
			if (methods == null)
				methods = new ArrayList<Method>(Arrays.asList(clazz.getDeclaredMethods()));
			
			for (int i = 0; i < methods.size(); i++) {
				Method method = methods.get(i);
				Column column = method.getAnnotation(Column.class);
				if (column != null)
					doCheck(column, method.getReturnType(), method.invoke(object));
				else
					methods.remove(i--);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		return list;
	}
	
	private List<String> doCheck(Column column, Class<?> type, Object value) {
		List<String> list = new ArrayList<String>();
		
		// If the column is not nullable
		if ( !column.nullable() ) {
			if (type == String.class) {
				String s = (String) value;
				if (s == null || s.isEmpty())
					list.add("Column '" + column.name() + "' cannot be null or empty.");
			} else {
				if (value == null)
					list.add("Column '" + column.name() + "' cannot be null.");
			}
		}
		
		// Check for maximum allowable length
		int length = column.length();
		if (type == String.class) {
			String s = (String) value;
			if (s != null && s.length() > length)
				list.add("Column '" + column.name() + "' must not exceed " + length + " characters.");
		}
		
		return list;
	}
	
}
